
#ifndef _SERVER_FILE_H_
#define _SERVER_FILE_H_

#include "md4.h"

class Client;
// we skip:
//		the message size 	(uint8)		(1)
//		the message id 		(uint8)		(1)
//		compressed flag		(uint8)		(1)
//		the block id		(uint32)	(4)
// ------------------------------------------
//										(7)
//
// and start writing in the raw data at the end of the message...
#define BLOCK_HEADERSIZE (7)

class CServerFile
{
	public:
		CServerFile();
		~CServerFile();

		bool8 InitState();
		bool8 Done();

		bool8 DoneChecksums();

		bool8 SetNumBlocks(uint32 num);
		uint32 GetNumBlocks();

		void SetBuffer(uint8 *buffer, uint32 bufsize);

		int32 Digest(uint32 amount, uint8 *obuf, int32 obufsize);
		uint8 *GetOBuf();
		int32 GetBlockID();

		// NOTE!  the block size MUST be a multiple of 512 BITS or
		//  64 BYTES!!!
		enum {BLOCK_SIZE = 512};

		// these are made public for speed and easy access from the
		//  rsdlpkg protocol.
		uint32	*m_weak_checksums;
		t_md4	*m_md4_checksums;

	protected:

		typedef struct t_weak_and_strong
		{
			uint32 *weak;
			t_md4  *strong;
		};

		bool8 CreateBlockMap(uint32 *new_weak_checksums);
		uint32 FindWeakCS(uint32 weakCS, uint32 *checksums);
		static int checksum_compare(const void *c1, const void *c2);

		bool8 SortChecksums();
		bool8 SetupHashTable();

		uint32 FindRollingChecksum(uint8 *buffer);
		bool8 FindStrongChecksum(t_md4 md4, uint8 *buffer);

		bool8 FoundBlock(uint32 found_block);
		bool8 MissedBlock(uint8 missed_byte);

	protected:

		// buffer that the file is read into and we use for calculating
		//  the rolling checksums
		uint8 *m_buffer;
		uint32 m_bufsize;

		uint8 *m_obuf;

		uint32	m_num_blocks;
		uint32 	*m_block_map;

		uint32 m_a, m_b;
		uint8 m_last_byte;

		// this bool is used to determine if we need to calculate our
		//  A and B values from the block or if we can reuse their 
		//  previous values.
		bool8 m_can_roll;

		int32 m_block_id;

		enum {HASH_SIZE = 65536};
		uint32 *m_hash_table[HASH_SIZE];
};

inline void 
CServerFile::SetBuffer(uint8 *buffer, uint32 bufsize)
{
	m_buffer = buffer;
	m_bufsize = bufsize;
}

inline bool8
CServerFile::SetNumBlocks(uint32 num)
{
	m_num_blocks = num;

	delete [] m_weak_checksums;
	m_weak_checksums = (uint32 *)0;
	delete [] m_md4_checksums;
	m_md4_checksums = (t_md4 *)0;

    if (m_num_blocks)
    {
        m_weak_checksums = new uint32[m_num_blocks];
	    if(!m_weak_checksums)
	    {
		    return false;
	    }

	    m_md4_checksums = new t_md4[m_num_blocks];
	    if(!m_md4_checksums)
	    {
		    return false;
	    }
    }

	return true;
}

inline uint32
CServerFile::GetNumBlocks()
{
	return m_num_blocks;
}

inline uint8 *
CServerFile::GetOBuf()
{
	return m_obuf;
}

inline int32
CServerFile::GetBlockID()
{
	return m_block_id;
}

#endif


